import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.model_selection import train_test_split

# ======================
# 0. 加载清洗后的数据
# ======================
df = pd.read_csv("cleaned_tourism_data.csv")
df['Sites Visited'] = df['Sites Visited'].str.split(', ')  # 确保转换为列表格式

# ======================
# 1. 创建综合评分系统
# ======================
# 处理缺失值（不使用inplace方法）
df['Tourist Rating'] = pd.to_numeric(df['Tourist Rating'], errors='coerce').fillna(df['Tourist Rating'].mean())
df['Satisfaction'] = pd.to_numeric(df['Satisfaction'], errors='coerce').fillna(df['Satisfaction'].mean())

# 计算标准化评分
df['Normalized_Rating'] = (df['Tourist Rating'] - df['Tourist Rating'].min()) / (
            df['Tourist Rating'].max() - df['Tourist Rating'].min())
df['Normalized_Satisfaction'] = (df['Satisfaction'] - df['Satisfaction'].min()) / (
            df['Satisfaction'].max() - df['Satisfaction'].min())

# 生成综合评分（0-5分范围）
df['Composite_Score'] = (0.5 * df['Normalized_Rating'] + 0.5 * df['Normalized_Satisfaction']) * 5

# 验证列存在性
assert 'Composite_Score' in df.columns, "综合评分列未正确生成！"

# ======================
# 2. 构建用户-景点评分矩阵
# ======================
# 展开嵌套列表
exploded_df = df.explode('Sites Visited').reset_index(drop=True)
ratings_df = exploded_df[['Tourist ID', 'Sites Visited', 'Composite_Score']]

# 过滤有效用户（至少访问过2个景点）
user_counts = ratings_df['Tourist ID'].value_counts()
valid_users = user_counts[user_counts >= 2].index
filtered_ratings = ratings_df[ratings_df['Tourist ID'].isin(valid_users)]

# 创建用户-物品评分矩阵
user_item_matrix = filtered_ratings.pivot_table(
    index='Tourist ID',
    columns='Sites Visited',
    values='Composite_Score',
    aggfunc='mean',
    fill_value=0
)

# ======================
# 3. 数据集拆分（随机拆分）
# ======================
users = filtered_ratings['Tourist ID'].unique()
train_users, test_users = train_test_split(
    users,
    test_size=0.2,
    random_state=42  # 移除stratify参数
)

# 生成数据集
train_ratings = filtered_ratings[filtered_ratings['Tourist ID'].isin(train_users)]
test_ratings = filtered_ratings[filtered_ratings['Tourist ID'].isin(test_users)]

print(f"\n数据集验证：")
print(f"总用户数：{len(users)}")
print(f"训练用户数：{len(train_users)}, 记录数：{len(train_ratings)}")
print(f"测试用户数：{len(test_users)}, 记录数：{len(test_ratings)}")

# ======================
# 4. 计算用户相似度矩阵
# ======================
user_sim = cosine_similarity(user_item_matrix)
user_sim_df = pd.DataFrame(
    user_sim,
    index=user_item_matrix.index,
    columns=user_item_matrix.index
)


# ======================
# 5. 协同过滤预测函数
# ======================
def predict_rating(user_id, site, k=3):
    """预测用户对指定景点的评分"""
    if user_id not in user_sim_df.index:
        return 0  # 处理新用户

    similar_users = user_sim_df[user_id].sort_values(ascending=False)[1:k + 1]
    site_ratings = user_item_matrix.loc[similar_users.index, site]

    weighted_sum = (similar_users.values * site_ratings.values).sum()
    sum_weights = np.abs(similar_users.values).sum()

    return weighted_sum / sum_weights if sum_weights != 0 else 0


# ======================
# 6. 模型评估
# ======================
def evaluate_model(test_data, k=3):
    y_true = []
    y_pred = []

    for _, row in test_data.iterrows():
        pred = predict_rating(row['Tourist ID'], row['Sites Visited'], k)
        y_true.append(row['Composite_Score'])
        y_pred.append(pred)

    rmse = np.sqrt(np.mean((np.array(y_true) - np.array(y_pred)) ** 2))
    mae = np.mean(np.abs(np.array(y_true) - np.array(y_pred)))
    return rmse, mae


# 执行评估
rmse, mae = evaluate_model(test_ratings)
print(f"\n模型评估结果：")
print(f"RMSE: {rmse:.3f}")
print(f"MAE: {mae:.3f}")


# ======================
# 7. 推荐生成
# ======================
def get_recommendations(user_id, n=5, k=3):
    """生成Top-N景点推荐"""
    visited = set(filtered_ratings[filtered_ratings['Tourist ID'] == user_id]['Sites Visited'])
    all_sites = user_item_matrix.columns

    recommendations = []
    for site in all_sites:
        if site not in visited:
            pred = predict_rating(user_id, site, k)
            recommendations.append((site, pred))

    return sorted(recommendations, key=lambda x: x[1], reverse=True)[:n]


# 示例推荐（使用实际存在的用户ID）
sample_user = train_users[0] if len(train_users) > 0 else test_users[0]
print(f"\n为用户 {sample_user} 的Top-3推荐：")
print(get_recommendations(sample_user, 3))